<template>
  <view class="calendar">
    <view class="calendar-box">
      <view class="calendar-wrapper">
        <view class="calendar-toolbar" v-if="cellheader">
          <view class="prev" @click="prevMonth">&lt;</view>
          <view class="current">{{ currentDateStr }}</view>
          <view class="next" @click="nextMonth">></view>
        </view>
        <slot v-else name="cell-header" :date="slotcurrentDateStr"></slot>
        <view class="calendar-week">
          <view class="week-item" v-for="item of weekList" :key="item">
            {{ item }}
          </view>
        </view>
        <view>
          <view class="calendar-inner" v-for="(item, index) of calendarList" :key="index">
            <view
              class="calendar-item"
              v-for="(dataItem, dateIndex) of item"
              :key="dateIndex"
              :class="{
                'calendar-item': true,
                'calendar-item-disabled': dataItem.disable,
                'calendar-item-checked': dataItem.date === '今天',
              }"
              @click="handleClickDay(dataItem)"
            >
              <view class="date">{{ dataItem.date }}</view>
              <slot name="cell-extra" :date="dataItem.year + '-' + (dataItem.slotmonth + 1) + '-' + dataItem.slotdate"></slot>
            </view>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>
<script>
  /**
   * ve-modal 弹框
   * @property {Boolean} maskClose 遮罩点击是否关闭
   * @property {Array} pricelist 价格list （可为空）
   */
  import dayjs from 'dayjs'

  export default {
    name: 'calendar',
    props: {
      pricelist: {
        type: Array,
        default: () => [],
      },
      cellheader: {
        type: Boolean,
        default: true,
      },
      initTime: {
        type: String,
        default: '',
      },
    },
    data() {
      return {
        showYearMonth: {}, // 显示的年月
        calendarList: [], // 用于遍历显示
        shareDate: new Date(), // 享元模式，用来做 日期数据转换 优化
        dayChecked: {}, // 当前选择的天
        weekList: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], // 周
      }
    },
    created() {
      this.initDataFun() // 初始化数据
    },
    computed: {
      // 显示当前时间
      currentDateStr() {
        let { year, month } = this.showYearMonth
        return `${year}年${this.pad(month + 1)}月`
      },
      slotcurrentDateStr() {
        let { year, month } = this.showYearMonth
        return `${year}-${month + 1}`
      },
    },
    methods: {
      //#region 计算日历数据
      // 初始化数据
      initDataFun() {
        // 初始化当前时间
        let time = this.initTime ? new Date(dayjs(this.initTime).$y,dayjs(this.initTime).$M,dayjs(this.initTime).$D) : new Date()
        this.setCurrentYearMonth(time) // 设置日历显示的日期（年-月）
        this.createCalendar() // 创建当前月对应日历的日期数据
        this.getCurrentDay() // 获取今天
      },
      // 设置日历显示的日期（年-月）
      setCurrentYearMonth(d = new Date()) {
        let year = d.getFullYear()
        let month = d.getMonth()
        let date = d.getDate()
        this.showYearMonth = {
          year,
          month,
          date,
        }
      },
      getCurrentDay(d = new Date()) {
        let year = d.getFullYear()
        let month = d.getMonth()
        let date = d.getDate()
        this.dayChecked = {
          year,
          month,
          date,
          value: this.stringify(year, month, date),
          disable: false,
        }
      },
      // 创建当前月对应日历的日期数据
      createCalendar() {
        const oneDayMS = 24 * 60 * 60 * 1000
        let list = []
        let { year, month } = this.showYearMonth
        // ---------------计算某月前后需要填补的天---------------
        // 当前月份第一天是星期几, 0-6
        let firstDay = this.getFirstDayByMonths(year, month)
        // 填充多少天
        let prefixDaysLen = firstDay === 0 ? 0 : firstDay
        // 向前移动之后的毫秒数
        let begin = new Date(year, month, 1).getTime() - oneDayMS * prefixDaysLen
        // 当前月份最后一天是星期几, 0-6
        let lastDay = this.getLastDayByMonth(year, month)
        // 填充多少天
        let suffixDaysLen = lastDay === 0 ? 0 : 7 - lastDay
        // 向后移动之后的毫秒数
        let end = new Date(year, month + 1, 0).getTime() + oneDayMS * suffixDaysLen
        // 填充天
        while (begin < end) {
          // 享元模式，避免重复 new Date
          this.shareDate.setTime(begin)
          let year = this.shareDate.getFullYear()
          let curMonth = this.shareDate.getMonth()
          let date = this.shareDate.getDate()
          let status = new Date().getFullYear() === year && new Date().getMonth() === curMonth && new Date().getDate() === date
          list.push({
            year: year,
            month: curMonth + 1, // 月是从0开始的
            date: status ? '今天' : this.pad(date),
            slotdate: date,
            slotmonth: curMonth,
            value: this.stringify(year, curMonth, date),
            disable: curMonth !== month,
            price:
              new Date().getFullYear() === year && new Date().getMonth() === curMonth && this.pricelist[date]
                ? '$' + this.pricelist[date]
                : '',
          })
          begin += oneDayMS
        }
        this.calendarList = this.group(list, 7)
      },
      group(array, subGroupLength) {
        let index = 0
        let newArray = []
        while (index < array.length) {
          newArray.push(array.slice(index, (index += subGroupLength)))
        }
        return newArray
      },
      // 格式化时间
      stringify(year, month, date) {
        let str = [year, this.pad(month + 1), this.pad(date)].join('-')
        return str
      },
      // 对小于 10 的数字，前面补 0
      pad(str) {
        return str < 10 ? `0${str}` : str
      },
      // 点击上一月
      prevMonth() {
        this.showYearMonth.month--
        this.recalculateYearMonth() // 因为 month的变化 会超出 0-11 的范围， 所以需要重新计算
        this.createCalendar() // 创建当前月对应日历的日期数据
      },
      // 点击下一月
      nextMonth() {
        this.showYearMonth.month++
        this.recalculateYearMonth() // 因为 month的变化 会超出 0-11 的范围， 所以需要重新计算
        this.createCalendar() // 创建当前月对应日历的日期数据
      },
      // 重算：显示的某年某月
      recalculateYearMonth() {
        let { year, month, date } = this.showYearMonth

        let maxDate = this.getDaysByMonth(year, month)
        // 预防其他月跳转到2月，2月最多只有29天，没有30-31
        date = Math.min(maxDate, date)

        let instance = new Date(year, month, date)
        this.setCurrentYearMonth(instance)
      },
      // 判断当前月有多少天
      getDaysByMonth(year, month) {
        return new Date(year, month + 1, 0).getDate()
      },
      // 当前月的第一天是星期几
      getFirstDayByMonths(year, month) {
        return new Date(year, month, 1).getDay()
      },
      // 当前月的最后一天是星期几
      getLastDayByMonth(year, month) {
        return new Date(year, month + 1, 0).getDay()
      },

      // 操作：点击了某天
      handleClickDay(item) {
        if (!item || item.disable) return
        this.dayChecked = item
      },
    },
  }
</script>
<style lang="scss" scoped>
  .calendar-box {
    display: flex;
    justify-content: center;
    align-items: center;
    background: #fff;

    .calendar-wrapper {
      width: 100%;
      .calendar-toolbar {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 28rpx 32rpx;
        .current {
          font-size: 36rpx;
          font-weight: 500;
          color: #2d2727;
          line-height: 44rpx;
        }
      }
      .calendar-week {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-between;
        padding: 0 24rpx;
        .week-item {
          width: 60rpx;
          font-size: 28rpx;
          font-weight: 400;
          color: #8491a8;
          line-height: 34rpx;
          margin-bottom: 32rpx;
        }
      }
      .calendar-inner {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-around;
        .calendar-item {
          width: 60rpx;
          font-size: 28rpx;
          font-weight: 400;
          color: #2d2727;
          line-height: 34rpx;
          margin-bottom: 32rpx;
          .date {
            width: 60rpx;
            text-align: center;
          }
          .price {
            width: 60rpx;
            text-align: center;
            margin-top: 6rpx;
            font-size: 24rpx;
            font-weight: 400;
            color: #8491a8;
            line-height: 24rpx;
          }
        }
        .calendar-item-hover {
          cursor: pointer;
          &:hover {
            color: #fff;
            background-color: #438bef;
          }
        }
        .calendar-item-disabled {
          font-size: 30rpx;
          font-weight: 400;
          color: #999999;
          line-height: 36rpx;
        }
        .calendar-item-checked {
          font-size: 30rpx;
          font-weight: 600;
          color: #3072f6;
          line-height: 36rpx;
        }
      }
    }
  }
</style>
